# Copyright (c) 2006-2007 The Regents of The University of Michigan
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met: redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer;
# redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution;
# neither the name of the copyright holders nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Authors: Gou Pengfei
# 	   

import os
import optparse
import sys
from os.path import join as joinpath

import m5
from m5.defines import buildEnv
from m5.objects import *
from m5.util import addToPath, fatal


if buildEnv['FULL_SYSTEM']:
    fatal("This script requires syscall emulation mode (*_SE).")

config_root = joinpath(os.environ['HOME'], 'M5-EDGE', 'configs')

addToPath(joinpath(config_root, 'common'))

import Simulation
from Caches import *
from cpu2000 import *

# This is the name of this configuration. This should be the same as name of this file
conf = ((os.path.basename(sys.argv[0])).split('.'))[0]

# Get paths we might need.  It's expected this file is in m5/configs/example.
# config_path = os.path.dirname(os.path.abspath(__file__))
# config_root = os.path.dirname(config_path)
m5_root = os.path.dirname(config_root)

parser = optparse.OptionParser()

execfile(os.path.join(config_root, "common", "Options.py"))

(options, args) = parser.parse_args()

# Define Cache Class
class MyCache(BaseCache):
    assoc = 2
    block_size = 64
    latency = '1ps'
    mshrs = 8
    tgts_per_mshr = 5

# Define CPU, system and Memory Hierarchy
cpu = SimpleEdgeCPU(cpu_id = 0,
		    numThreads = 1,
		    numIQEntries = 1024,
		    exitPredType = 'TargetOnly',
                    btypePredType = 'Twostage',
                    btypeTwostagePredictionMode = 'TourAndHyst',
                    btypeTourBinaryPredictionMode = 'CPathLocal',
                    btypeTableMode = 'Normal',
		    SQEntries = 256,
		    LQEntries = 256,
                    simPointSim = True,
                    preExecuteMode = True,
                    memDepMode = 'Perfect',
                    predicationMode = 'Perfect')

##CONFIG
cpu.binaryLocalPredictorSize = 4096
cpu.binaryLocalHistoryTableSize = 1024
cpu.binaryLocalHistoryBits = 12
cpu.binaryLocalCtrBits = 2
cpu.binaryGlobalPredictorSize = 8192
cpu.binaryGlobalHistoryBits = 13
cpu.binaryGlobalCtrBits = 2
cpu.binaryChoicePredictorSize = 8192
cpu.binaryChoiceHistoryBits = 13
cpu.binaryChoiceCtrBits = 3
cpu.globalPredictorSize = 4096
cpu.btbType = 'NewBTB';
cpu.BTBIndexType = 'BinaryHistory';
cpu.BTBTableMode = 'NormalTable';
cpu.BTBHistoryLength = 12;
cpu.BTBEntries = 4096;
cpu.CTBEntries = 128;
cpu.RASSize = 128;

cpu.addTwoLevelCacheHierarchy(MyCache(size='64kB'),
                              MyCache(size='32kB'),
                              MyCache(size='2MB'))

cpu.toL2Bus.clock = '1THz'
                              
cpu.clock = '2GHz'

system = System(cpu = cpu,
                physmem = PhysicalMemory(latency = '1ps',
                                         range = AddrRange(Addr('0MB'),
                                                           size = '256MB')),
                membus = Bus(clock='1THz'),
                mem_mode = 'atomic')

system.physmem.port = system.membus.port
cpu.connectMemPorts (system.membus)

# Define pre execute cpu and its system.
p_cpu = AtomicEdgeCPU(cpu_id = 0,
                    maxPass = 256,
                    preExecuteMode = True,
                    simPointSim = True 
                    )
                              
p_cpu.clock = cpu.clock

p_system = System(cpu = p_cpu,
                  physmem = PhysicalMemory(latency = '1ps',
                                           range = AddrRange(Addr('0MB'),
                                           size = '256MB')),
                  membus = Bus(clock='1THz'),
                  mem_mode = 'atomic')

p_system.cpu.physmem_port = p_system.physmem.port

p_system.physmem.port = p_system.membus.port
p_cpu.connectMemPorts(p_system.membus)

cpu.preExecuteCPU = p_cpu

root = Root(system = [system,p_system])

# Since we're in batch mode, dont allow tcp socket connections
m5.disableAllListeners()

# set default maxtick... script can override
# -1 means run forever
maxtick = m5.MaxTick

# Define the spec cpu 2000 workload, set the simpoint parameters
if options.bench:

    if not options.input_set:
        print >> sys.stderr, "Don't know what input set to use"
        sys.exit(1)
        
    try:
        # Setup workload for the detailed cpu
        exec("workload0 = %s('trips', 'linux', '%s', '%s', 'Omax_ei')" % (options.bench, options.input_set, conf))
        process0 = workload0.makeLiveProcess()

        # Set simpoint
        cpu.simPointStartFunctionName = workload0.edgeSimpointStartFunctionName
        cpu.simPointEndFunctionName = workload0.edgeSimpointEndFunctionName
        cpu.simPointCallNum = workload0.edgeSimpointStartFunctionCallNum
        cpu.simPointRetNum = workload0.edgeSimpointEndFunctionRetNum
        cpu.simPointMode = "EndPoint"

        print 
        print "Making checkpoint at name:%s; num of calls:%d" % \
                (cpu.simPointStartFunctionName,cpu.simPointCallNum)
        print "The end of this simpoint is at name:%s; num of rets:%d" % \
                (cpu.simPointEndFunctionName,cpu.simPointRetNum)
        print

        # Setup workload for the pre-execute cpu
        exec("workload1 = %s('trips', 'linux', '%s', '%s-pre-exe', 'Omax_ei')" % (options.bench, \
             options.input_set, conf))
        process1 = workload1.makeLiveProcess()

        # Set simpoint
        p_cpu.simPointStartFunctionName = workload1.edgeSimpointStartFunctionName
        p_cpu.simPointEndFunctionName = workload1.edgeSimpointEndFunctionName
        p_cpu.simPointCallNum = workload1.edgeSimpointStartFunctionCallNum
        p_cpu.simPointRetNum = workload1.edgeSimpointEndFunctionRetNum
        p_cpu.simPointMode = "EndPoint"

        print 
        print "Making checkpoint at name:%s; num of calls:%d" % \
                (p_cpu.simPointStartFunctionName,p_cpu.simPointCallNum)
        print "The end of this simpoint is at name:%s; num of rets:%d" % \
                (p_cpu.simPointEndFunctionName,p_cpu.simPointRetNum)
        print
 
    except:
        print >>sys.stderr, "Unable to find workload for %s" % options.bench
        sys.exit(1)
       
# tweak configuration for specific test
root.system[0].cpu.workload = process0
root.system[1].cpu.workload = process1

# instantiate configuration
m5.instantiate(root)

from m5.stats import *

# Restore checkpoint and simulate!
m5.restoreCheckpoint(root,\
        '%s/workdir/checkpoint/cpt.spt.3.2.Omax_ei.%s.multi-system' % (os.environ['HOME'],options.bench))

# Connect MySQL db.
# if you prefer to put your results into MySQL db,
# recomment the following lines

#m5.stats.initMySQL(host='202.118.229.70',
#                   database='yourdb',
#                   user='yourname',passwd='yourpasswd', 
#                   project='spec2000',name=options.bench + '-' + conf)

exit_event = m5.simulate()

print 'Exiting @ tick', m5.curTick(), 'because', exit_event.getCause()
